
/*
	pbrt source code Copyright(c) 1998-2012 Matt Pharr and Greg Humphreys.

	This file is part of pbrt.

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions are
	met:

	- Redistributions of source code must retain the above copyright
	notice, this list of conditions and the following disclaimer.

	- Redistributions in binary form must reproduce the above copyright
	notice, this list of conditions and the following disclaimer in the
	documentation and/or other materials provided with the distribution.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
	IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
	PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
	HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
	LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
	DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
	THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

	*/


#include "stdafx.h"
#include "renderers/groundtruth.h"
#include "scene.h"
#include "film.h"
#include "volume.h"
#include "sampler.h"
#include "integrator.h"
#include "progressreporter.h"
#include "camera.h"
#include "intersection.h"


GroundTruthTask::GroundTruthTask(const Scene *sc, Renderer *ren, Camera *c,
	ProgressReporter &pr, Sampler *ms, Sample *sam, bool visIds,
	int tn, int tc) : 
	SamplerRendererTask(sc, ren, c, pr, ms, sam, visIds, tn, tc) {}


void GroundTruthTask::Run() {
	PBRT_STARTED_RENDERTASK(taskNum);
	// Get sub-_Sampler_ for _SamplerRendererTask_
	Sampler *sampler = mainSampler->GetSubSampler(taskNum, taskCount);
	if (!sampler)
	{
		reporter.Update();
		PBRT_FINISHED_RENDERTASK(taskNum);
		return;
	}

	// Declare local variables used for rendering loop
	MemoryArena arena;
	RNG rng(taskNum);

	// Allocate space for samples and intersections
	int maxSamples = sampler->MaximumSampleCount();
	Sample *samples = origSample->Duplicate(maxSamples);
	RayDifferential *rays = new RayDifferential[maxSamples];
	IntegrationResult *results = new IntegrationResult[maxSamples];
	Spectrum *Ls = new Spectrum[maxSamples];
	Spectrum *Ts = new Spectrum[maxSamples];
	Intersection *isects = new Intersection[maxSamples];

	// Get samples from _Sampler_ and update image
	int sampleCount;
	while ((sampleCount = sampler->GetMoreSamples(samples, rng)) > 0) {
		// Generate camera rays and compute radiance along rays
		for (int i = 0; i < sampleCount; ++i) {
			// Find camera ray for _sample[i]_
			PBRT_STARTED_GENERATING_CAMERA_RAY(&samples[i]);
			float rayWeight = camera->GenerateRayDifferential(samples[i], &rays[i]);
			rays[i].ScaleDifferentials(1.f / sqrtf(sampler->samplesPerPixel));
			PBRT_FINISHED_GENERATING_CAMERA_RAY(&samples[i], &rays[i], rayWeight);

			// Evaluate radiance along camera ray
			PBRT_STARTED_CAMERA_RAY_INTEGRATION(&rays[i], &samples[i]);
			if (visualizeObjectIds) {
				if (rayWeight > 0.f && scene->Intersect(rays[i], &isects[i])) {
					// random shading based on shape id...
					uint32_t ids[2] = { isects[i].shapeId, isects[i].primitiveId };
					uint32_t h = hash((char *)ids, sizeof(ids));
					float rgb[3] = { float(h & 0xff), float((h >> 8) & 0xff),
						float((h >> 16) & 0xff) };
					results[i].L = Spectrum::FromRGB(rgb);
					results[i].L /= 255.f;
				}
				else
					results[i].L = 0.f;
			}
			else {
				if (rayWeight > 0.f) {
					results[i] = renderer->Li(scene, rays[i], &samples[i], rng,
						arena, &isects[i], &Ts[i]);
					results[i].L *= rayWeight;
					for (int j = 0; j < results[i].histogramSamples.size(); j++) {
						results[i].histogramSamples[j].radiance *= rayWeight;
					}
				}
				else {
					results[i].L = 0.f;
					Ts[i] = 1.f;
				}

				// Issue warning if unexpected radiance value returned
				if (results[i].L.HasNaNs()) {
					Error("Not-a-number radiance value returned "
						"for image sample.  Setting to black.");
					results[i].L = Spectrum(0.f);
				}
				else if (results[i].L.y() < -1e-5) {
					Error("Negative luminance value, %f, returned "
						"for image sample.  Setting to black.", results[i].L.y());
					results[i].L = Spectrum(0.f);
				}
				else if (isinf(results[i].L.y())) {
					Error("Infinite luminance value returned "
						"for image sample.  Setting to black.");
					results[i].L = Spectrum(0.f);
				}
				Ls[i] = results[i].L;
			}
			PBRT_FINISHED_CAMERA_RAY_INTEGRATION(&rays[i], &samples[i], &Ls[i]);
		}

		// Report sample results to _Sampler_, add contributions to image
		if (sampler->ReportResults(samples, rays, Ls, isects, sampleCount))
		{
			for (int i = 0; i < sampleCount; ++i)
			{
				PBRT_STARTED_ADDING_IMAGE_SAMPLE(&samples[i], &rays[i], &Ls[i], &Ts[i]);

				int nBounces = results[i].histogramSamples.size();

				PaintData data;
				data.L = results[i].L;
				for (int j = 0; j < nBounces; j++) {
					data.bounces.push_back(results[i].histogramSamples[j]);
				}
                this->camera->film->Splat(samples[i], data);
				
				PBRT_FINISHED_ADDING_IMAGE_SAMPLE();
			}
		}

		// Free _MemoryArena_ memory from computing image sample values
		arena.FreeAll();
	}

	// Clean up after _SamplerRendererTask_ is done with its image region
	camera->film->UpdateDisplay(sampler->xPixelStart,
		sampler->yPixelStart, sampler->xPixelEnd + 1, sampler->yPixelEnd + 1);
	delete sampler;
	delete[] samples;
	delete[] rays;
	delete[] results;
	delete[] Ts;
	delete[] isects;
	reporter.Update();
	PBRT_FINISHED_RENDERTASK(taskNum);
}


GroundTruthRenderer::GroundTruthRenderer(Sampler *s, Camera *c,
	SurfaceIntegrator *si, VolumeIntegrator *vi, bool visIds) :
	SamplerRenderer(s, c, si, vi, visIds) {}


void GroundTruthRenderer::Render(const Scene *scene) {
	PBRT_FINISHED_PARSING();
	// Allow integrators to do preprocessing for the scene
	PBRT_STARTED_PREPROCESSING();
	surfaceIntegrator->Preprocess(scene, camera, this);
	volumeIntegrator->Preprocess(scene, camera, this);
	PBRT_FINISHED_PREPROCESSING();
	PBRT_STARTED_RENDERING();
	// Allocate and initialize _sample_
	Sample *sample = new Sample(sampler, surfaceIntegrator,
		volumeIntegrator, scene);

	// Create and launch _SamplerRendererTask_s for rendering image

	// Compute number of _SamplerRendererTask_s to create for rendering
	int nPixels = camera->film->xResolution * camera->film->yResolution;
	int nTasks = max(32 * NumSystemCores(), nPixels / (16 * 16));
	nTasks = RoundUpPow2(nTasks);
	ProgressReporter reporter(nTasks, "Rendering");
	vector<Task *> renderTasks;
	for (int i = 0; i < nTasks; ++i)
		renderTasks.push_back(new GroundTruthTask(scene, this, camera,
		reporter, sampler, sample,
		visualizeObjectIds,
		nTasks - 1 - i, nTasks));
	EnqueueTasks(renderTasks);
	WaitForAllTasks();
	for (uint32_t i = 0; i < renderTasks.size(); ++i)
		delete renderTasks[i];
	reporter.Done();
	PBRT_FINISHED_RENDERING();
	// Clean up after rendering and store final image
	delete sample;
	camera->film->WriteImage();
}
